#!/usr/bin/python
# -*- coding: utf-8 -*-
# coding=utf-8
import math
import random
import multiprocessing
import threading
import datetimeDemo as tsLog

# 返回一个元组(落在圆内的点的数量，总的点数量，计算得到的pi值)
def MonteCarlo1M(resList=None, index=None):
    loop = 0
    inCount = 0
    outCount = 0
    pNum = 1000000 #1M

    # 使用蒙特卡洛办法估算pi。我们将 10,000,000 个随机点放入长为1的正方形中，如果点的坐标满足：x^2+y^2=1
    # 则表示点就落入到单位圆中，反之则落到圆外。由此我们可以估算出3位小数可靠的pi值。
    while loop<pNum:
        x = random.random()
        y = random.random()
        powerSum = x*x + y*y
        if powerSum < 1.0:
            inCount += 1
        else:
            outCount += 1
        loop += 1

        # if loop % 1000000 == 0:
        #     piValue = 4 * inCount/loop
        #     tsLog.logTs(f"{inCount} / {loop}, piValue = {piValue}")

    # ratio = inCount / pNum  # = PI*1^2/4 = PI / 4
    # piValue = 4*ratio
    # tsLog.logTs(f"piValue = {piValue}")

    print("resList ", type(resList))
    if resList and isinstance(resList, list):
        if index < len(resList):
            resList[index] = (inCount, pNum, 4*(inCount / pNum))
            return
    elif resList and isinstance(resList, multiprocessing.managers.ListProxy):
        # 用于多进程，向共享
        if index < len(resList):
            resList[index] = (inCount, pNum, 4*(inCount / pNum))
            return

    return (inCount, pNum, 4*(inCount / pNum))

def singleThread10MDemo():
    tsLog.logTs("singleThread10MDemo", preNewLineNum=1)
    inCountSum = 0
    allSum = 0
    for loop in range(10):
        res = MonteCarlo1M()
        inCountSum += res[0]
        allSum += res[1]
        tsLog.logTs(f"{loop}, {res}")
    ratio = inCountSum / allSum
    piValue = 4 * ratio
    tsLog.logTs(f"{inCountSum} / {allSum}, piValue = {piValue}")

def muliThread10MDemo():
    tsLog.logTs("muliThread10MDemo", preNewLineNum=1)
    resList = []
    threadList = []
    # 开启10个线程
    for loop in range(10):
        resList.append((0,0,0))
        tm = threading.Thread(target=MonteCarlo1M, args=(resList, loop))
        threadList.append(tm)
        tm.start()
        tsLog.logTs(f"{loop}, thread start, {tm}")

    tsLog.logTs(f"all thread star")

    # 等待10个线程 计算结束
    for t in threadList:
        t.join()

    tsLog.logTs(f"all thread join, {resList}")

    # 收集计算结果
    inCountSum = 0
    allSum = 0
    for res in resList:
        inCountSum += res[0]
        allSum += res[1]
    ratio = inCountSum / allSum
    piValue = 4 * ratio
    tsLog.logTs(f"{inCountSum} / {allSum}, piValue = {piValue}")

def muliProcess10MDemo():
    tsLog.logTs("muliProcess10MDemo", preNewLineNum=1)

    resList = multiprocessing.Manager().list()
    prcList = []
    print("Manager().list()", type(resList))
    for index in range(10):
        resList.append((0, 0, 0))
        prc = multiprocessing.Process(target=MonteCarlo1M, args=(resList, index))
        prcList.append(prc)
        prc.start()
        tsLog.logTs(f"{index}, thread start, {prc}")

    tsLog.logTs(f"all process star")

    for p in prcList:
        p.join()

    tsLog.logTs(f"all process join, {resList}")

    # 收集计算结果
    inCountSum = 0
    allSum = 0
    for res in resList:
        inCountSum += res[0]
        allSum += res[1]
    ratio = inCountSum / allSum
    piValue = 4 * ratio
    tsLog.logTs(f"{inCountSum} / {allSum}, piValue = {piValue}")


def demo():
    tsLog.logTs("multiProcessDemo", preNewLineNum=2)
    print("cpu 核心数", multiprocessing.cpu_count())

    # 单线程调用10遍 1M蒙特卡洛，加和结果 计算π
    singleThread10MDemo()

    # 多线程，每个线程调用1M蒙特卡洛方法，向同一个内存列表中写入结果，主线程加和结果 计算π
    # 这个并不比单线程快，因为 GIL, python 的多线程其实只能使用1个cpu核心
    muliThread10MDemo()

    # 多进程，每个进程调用1M蒙特卡洛方法，向共享列表中写入结果，主进程加和结果 计算π
    # 这个比单线程快得多，绕过了GIL, 真正同时使用到多个cpu核心
    muliProcess10MDemo()

if __name__ == '__main__':
    demo()